//*************************************************************************************************
// LOCKS.H
//
// This class provides a number of locking primitives for multi-threaded programming.  The main
// class of interest are:
//	CCritLock			Critical section based lock wrapper class.
//	CAutoLock			A helper class to lock/unlock in ctor/dtor.
//*************************************************************************************************
#ifndef __LOCKS_H__
#define __LOCKS_H__

//*************************************************************************************************
// This lock class is based on NT's critical sections and has all of their semantics.
//*************************************************************************************************
class CCritLock
{
private:
	CRITICAL_SECTION m_sCrit;				// The critical section to block on.
	int				m_iLocks;				// Count of locks.
	#ifdef _DEBUG
	BOOL			m_bInit;				// Track init status.
	#endif

public:
	inline CCritLock()
	{ 
		#ifdef _DEBUG
		m_bInit = TRUE;
		#endif
		m_iLocks = 0;
		InitializeCriticalSection(&m_sCrit); 
	}
	
	inline ~CCritLock()
	{ 
		assert(m_bInit);
		assert(m_iLocks == 0);
		DeleteCriticalSection(&m_sCrit); 
	}
	
	inline void Lock()
	{ 
		assert(m_bInit);
		EnterCriticalSection(&m_sCrit);
		++m_iLocks;
		assert(m_iLocks > 0);
	}

	inline void Unlock()
	{
		assert(m_bInit);
		assert(m_iLocks >= 0);
		--m_iLocks;
		LeaveCriticalSection(&m_sCrit);
	}

	inline int GetLockCnt()
		{ return m_iLocks; }
	inline BOOL IsLocked()
		{ return m_iLocks != 0; }
};

//*************************************************************************************************
// This helper class automatically locks the given lock object in the ctor and frees it in the
// dtor.  This makes your code slightly cleaner by not requiring an unlock in all failure conditions.
//*************************************************************************************************
class CAutoLock
{
	CCritLock		*m_psCrit;				// Crit lock.
	int				m_iNest;				// Nesting count for the item.

public:
	//*********************************************************************************************
	// Use this ctor with the assignment operators to do deffered locking.
	//*********************************************************************************************
	CAutoLock() :
		m_psCrit(NULL),
		m_iNest(0)
	{
	}

	//*********************************************************************************************
	// This version handles a critical section lock.
	//*********************************************************************************************
	CAutoLock(CCritLock *psLock) :
		m_psCrit(psLock),
		m_iNest(1)
	{ 
		assert(psLock != NULL);
		if (psLock)
		{
			psLock->Lock();
		}
	}
	
	//*********************************************************************************************
	// Free the lock we actually have.
	//*********************************************************************************************
	~CAutoLock()
	{
		// If we actually took a lock, unlock it.
		if (m_iNest != 0)
		{
			if (m_psCrit)
			{
				while (m_iNest--)
				{
					m_psCrit->Unlock();
				}
			}
		}
	}
};

#endif //  __LOCKS_H__
